package com.nkym.wypan.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nkym.wypan.constants.*;

import com.nkym.wypan.enums.FolderFlag;
import com.nkym.wypan.model.context.userfile.AddUserFolderContext;
import com.nkym.wypan.model.entity.User;
import com.nkym.wypan.mapper.UserMapper;

import com.nkym.wypan.model.entity.UserInfo;
import com.nkym.wypan.model.req.user.LoginReq;
import com.nkym.wypan.model.req.user.UserRegisterReq;

import com.nkym.wypan.service.UserFileService;
import com.nkym.wypan.service.UserInfoService;
import com.nkym.wypan.service.UserService;
import com.nkym.wypan.enums.ResponseStatus;
import com.nkym.wypan.exception.BusinessException;
import com.nkym.wypan.utils.IdUtils;
import com.nkym.wypan.utils.JwtUtils;
import com.nkym.wypan.utils.UserContextHolder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

import static com.nkym.wypan.constants.UserConstants.*;

/**
 * @author 19637
 * @description 针对表【wy_pan_user】的数据库操作Service实现
 * @createDate 2023-06-05 15:25:42
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {
    @Resource
    UserMapper userMapper;
    @Resource
    UserInfoService userInfoService;
    @Resource
    RedisTemplate<String, Object> redisTemplate;
    @Resource
    UserFileService userFileService;

    @Override
    public Long getUserId(String token) {
        boolean b = verifyToken(token);
        return b ? JwtUtils.getUidOrNull(token) : null;
    }

    @Override
    public void logout() {
        Long userId = UserContextHolder.getId();
        //清除token 登录态
        redisTemplate.delete(RedisKey.USER_LOGIN_KEY + userId);
    }

    @Override
    public UserInfo getUserInfo() {
        Long userId = UserContextHolder.getId();
        UserInfo userInfo = userInfoService.getById(userId);
        if (userInfo == null) {
            throw new BusinessException(ResponseStatus.USER_NOT_EXIST);
        }
        return userInfo;
    }

    @Override
    public String login(LoginReq loginReq) {
        String username = loginReq.getUsername();
        String password = loginReq.getPassword();

        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
        if (user == null) {
            throw new BusinessException(ResponseStatus.USER_NOT_EXIST);
        }
        //获取盐值
        String salt = user.getSalt();
        //盐值加密
        String encryptPassword = DigestUtil.md5Hex(password + salt);
        String realPassword = user.getPassword();
        //账号密码比对
        if (!realPassword.equals(encryptPassword)) {
            throw new BusinessException(ResponseStatus.USER_PASSWORD_ERROR);
        }
        Long uid = user.getUserId();
        String token = JwtUtils.createToken(uid);
        //放入Redis，用于token续期 ,设置7天过期
        redisTemplate.opsForValue().set(RedisKey.USER_LOGIN_KEY + uid, token, TOKEN_EXPIRES_TIME, TimeUnit.DAYS);

        return token;
    }

    @Override
    @Transactional
    public void register(UserRegisterReq userRegisterReq) {
        String username = userRegisterReq.getUsername();
        String password = userRegisterReq.getPassword();
        String question = userRegisterReq.getQuestion();
        String answer = userRegisterReq.getAnswer();
        //加锁 防止高并发下的脏数据出现
        synchronized (username.intern()) {
            User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
            if (user != null) {
                throw new BusinessException(ResponseStatus.USER_EXIST);
            }
        }
        //随机授予盐值 用于盐值加密
        String salt = RandomUtil.randomString(8);
        //盐值加密密码
        String encryptPassword = DigestUtil.md5Hex(password + salt);
        //雪花算法生成全局唯一ID
        long id = IdUtils.getId();
        //创建注册用户
        User registerUser = cover2User(id, username, encryptPassword, salt, question, answer);
        userMapper.insert(registerUser);
        User user = userMapper.selectById(id);
        if (user == null) {
            throw new BusinessException(ResponseStatus.USER_REGISTER_FAIL);
        }
        //初始化用户信息
        initUserInfo(user.getUserId());
        //分配初始化目录
        createFolder(user.getUserId());
    }

    /**
     * -------------------------------------------------private--------------------------------------------------------------
     */
    private boolean verifyToken(String token) {
        Long uid = JwtUtils.getUidOrNull(token);
        if (uid == null) {
            return false;
        }
        String userKey = RedisKey.USER_LOGIN_KEY + uid;
        String realToken = (String) redisTemplate.opsForValue().get(userKey);

        return token.equals(realToken);
    }

    private void createFolder(Long userId) {
        AddUserFolderContext context = new AddUserFolderContext();
        context.setUserId(userId);
        context.setFilename(FileConstants.ALL_FILE);
        context.setFolderFlag(FolderFlag.YES.getCode());
        context.setParentId(CommonConstants.Zero);
        userFileService.createFolder(context);
    }

    private void initUserInfo(Long userId) {
        //分配用户空间
        UserInfo userInfo = new UserInfo();
        userInfo.setAvatar("");
        userInfo.setNickname("用户_"+RandomUtil.randomString(8));
        userInfo.setUserId(userId);
        userInfo.setUseSpace(0L);
        userInfo.setTotalSpace(SpaceConstants.MB_50);
        if (!userInfoService.save(userInfo)) {
            throw new BusinessException(ResponseStatus.USER_REGISTER_FAIL);
        }
    }

    private User cover2User(Long userId, String username, String password, String salt, String question, String answer) {
        User user = new User();
        user.setUserId(userId);
        user.setUsername(username);
        user.setPassword(password);
        user.setSalt(salt);
        user.setQuestion(question);
        user.setAnswer(answer);
        return user;
    }

}




